import {Body, Controller, Get, Headers, HttpStatus, Post, Query, Res} from '@nestjs/common';
import {Response} from 'express';
import {CommonResponseDto} from '../dto/common.response.dto';
import {AppStatus, ResCode, UserStatus} from '../common/constants';
import {AppListService} from '../service/app.list.service';
import {UserListService} from '../service/user.list.service';
import {CommonRedisService} from '../service/common.redis.service';
import {formatTime, uuid} from "../common/utils";
import {
    decrypt,
    encrypt,
    generateRandomTokenBase64, getHeadersAccessToken,
    getHeadersApiToken,
    getModelConfigList,
    setErrorStreamRes
} from "../common/common";
import AppConfig from "../../config/app.config";

const getRedisAccessTokenKey = (appId: string, openId: string) => {
    return `${appId}_${openId}_accessToken`
}
const getRedisRefreshTokenKey = (appId: string, openId: string) => {
    return `${appId}_${openId}_refreshToken`
}
const getRedisCodeKey = (appId: string, userId: string) => {
    return `${appId}_${userId}_code`
}

/**
 * code：encrypt(`${appId}_${uuid(16)}_${userId}`, AppConfig.Auth2CodeSecretKey)
 * openId：encrypt(`${appId}_${AppConfig.Auth2OpenIdAppendStr}_${userId}`, AppConfig.Auth2OpenIdSecretKey)
 * accessToken：encrypt(`${appId}_${uuid(32)}_${openId}`, AppConfig.Auth2AccessTokenSecretKey)
 * refreshToken：encrypt(`${appId}_${uuid(32)}_${openId}`, AppConfig.Auth2RefreshTokenSecretKey)
 */
@Controller('open-api/auth')
export class Auth2Controller {
    constructor(private readonly userListService: UserListService,
                private readonly appListService: AppListService,
                private readonly commonRedisService: CommonRedisService) {}


    @Post('/code')
    async getCode(@Body() params: {appId: string, userId: string}, @Res({ passthrough: true }) res: Response) {
        res.status(HttpStatus.OK);
        if (!params.appId || !params.userId) {
            return new CommonResponseDto(ResCode.ERROR, '缺失参数')
        }
        try {
            const appDto = await this.appListService.getOneByAppId(params.appId)
            if (!appDto) {
                return new CommonResponseDto(ResCode.ERROR, 'app不存在')
            }
            if (appDto.status !== AppStatus.NORMAL) {
                return new CommonResponseDto(ResCode.ERROR, 'app状态非正常')
            }
        } catch(e){
            console.log(e)
            return new CommonResponseDto(ResCode.ERROR, '校验appId异常')
        }
        try {
            const userDto = await this.userListService.getOneById(params.userId)
            if (!userDto) {
                return new CommonResponseDto(ResCode.ERROR, 'user不存在')
            }
            if (userDto.status !== UserStatus.NORMAL) {
                return new CommonResponseDto(ResCode.ERROR, 'user状态非正常')
            }
        } catch(e){
            console.log(e)
            return new CommonResponseDto(ResCode.ERROR, '校验userId异常')
        }
        const code = encrypt(`${params.appId}_${uuid(16)}_${params.userId}`, AppConfig.Auth2CodeSecretKey)
        try {
            await this.commonRedisService.set(getRedisCodeKey(params.appId, params.userId), code, AppConfig.Auth2CodeExpireSec)
            return new CommonResponseDto(ResCode.OK, 'success', code)
        } catch(e){
            console.log(e)
            return new CommonResponseDto(ResCode.ERROR, '生成code异常')
        }
    }

    @Post('/access-token')
    async getAccessToken(@Body() params: {appId: string, appSecret: string, code: string}, @Res({ passthrough: true }) res: Response) {
        res.status(HttpStatus.OK);
        if (!params.appId || !params.appSecret || !params.code) {
            return new CommonResponseDto(ResCode.ERROR, '缺失参数')
        }
        const decryptStr = decrypt(params.code, AppConfig.Auth2CodeSecretKey)
        const strArr: string[] = decryptStr.split('_')
        if (strArr.length !== 3) {
            return new CommonResponseDto(ResCode.INVALID, 'code非法')
        }
        try {
            const appDto = await this.appListService.getOneByAppId(params.appId)
            if (!appDto || appDto.appSecret !== params.appSecret) {
                return new CommonResponseDto(ResCode.ERROR, 'app不存在或appSecret错误')
            }
            if (appDto.status !== AppStatus.NORMAL) {
                return new CommonResponseDto(ResCode.ERROR, 'app状态非正常')
            }
        } catch(e){
            console.log(e)
            return new CommonResponseDto(ResCode.ERROR, '获取app错误')
        }
        const userId = strArr[2]
        try {
            const redisCode = await this.commonRedisService.get(getRedisCodeKey(params.appId, userId))
            if (!redisCode || redisCode !== params.code) {
                return new CommonResponseDto(ResCode.EXPIRE, 'code已过期或不存在')
            }
            const openId = encrypt(`${params.appId}_${AppConfig.Auth2OpenIdAppendStr}_${userId}`, AppConfig.Auth2OpenIdSecretKey)
            const accessToken = encrypt(`${params.appId}_${uuid(32)}_${openId}`, AppConfig.Auth2AccessTokenSecretKey)
            const refreshToken = encrypt(`${params.appId}_${uuid(32)}_${openId}`, AppConfig.Auth2RefreshTokenSecretKey)
            try {
                await this.commonRedisService.set(getRedisAccessTokenKey(params.appId, openId), accessToken)
                await this.commonRedisService.set(getRedisRefreshTokenKey(params.appId, openId), refreshToken)
                await this.commonRedisService.del(getRedisCodeKey(params.appId, userId))
            } catch(e){}
            return new CommonResponseDto(ResCode.OK, 'success', {
                openId: openId,
                accessToken: accessToken,
                refreshToken: refreshToken,
                expiresIn: AppConfig.Auth2AccessTokenExpireSec
            })
        } catch(e){
            console.log(e)
            return new CommonResponseDto(ResCode.ERROR, '服务异常')
        }
    }

    @Get('/user-info')
    async getUserInfo(@Headers() headers: Record<string, string>, @Res({ passthrough: true }) res: Response) {
        res.status(HttpStatus.OK);
        const accessToken = getHeadersAccessToken(headers)
        if (!accessToken) {
            return new CommonResponseDto(ResCode.ERROR, '缺失accessToken参数')
        }
        let decryptStr = decrypt(accessToken, AppConfig.Auth2AccessTokenSecretKey)
        let strArr = decryptStr.split('_')
        if (strArr.length !== 3) {
            return new CommonResponseDto(ResCode.INVALID, 'accessToken非法')
        }
        const openId = strArr[2]
        try {
            decryptStr = decrypt(openId, AppConfig.Auth2OpenIdSecretKey)
            strArr = decryptStr.split('_')
            if (strArr.length !== 3) {
                return new CommonResponseDto(ResCode.INVALID, 'accessToken非法')
            }
            const userId = strArr[2]
            const userDto = await this.userListService.getOneById(userId)
            if (!userDto) {
                return new CommonResponseDto(ResCode.INVALID, 'accessToken非法')
            }
            return new CommonResponseDto(ResCode.OK, 'success', {
                openId,
                userAccount: userDto.userAccount,
                contact: userDto.contact,
                remark: userDto.remark,
                balance: userDto.balance,
                status: userDto.status,
            })
        } catch(e){
            console.log(e)
            return new CommonResponseDto(ResCode.ERROR, '服务异常')
        }
    }

    @Get('/model-token')
    async getModelToken(@Headers() headers: Record<string, string>, @Res({ passthrough: true }) res: Response) {
        res.status(HttpStatus.OK);
        const accessToken = getHeadersAccessToken(headers)
        if (!accessToken) {
            return new CommonResponseDto(ResCode.ERROR, '缺失accessToken参数')
        }
        let decryptStr = decrypt(accessToken, AppConfig.Auth2AccessTokenSecretKey)
        let strArr = decryptStr.split('_')
        if (strArr.length !== 3) {
            return new CommonResponseDto(ResCode.INVALID, 'accessToken非法')
        }
        const appId = strArr[0]
        const openId = strArr[2]
        try {
            const redisAccessToken = await this.commonRedisService.get(getRedisAccessTokenKey(appId, openId))
            if (!redisAccessToken) {
                return new CommonResponseDto(ResCode.EXPIRE, 'accessToken过期或不存在')
            }
            decryptStr = decrypt(openId, AppConfig.Auth2OpenIdSecretKey)
            strArr = decryptStr.split('_')
            if (strArr.length !== 3) {
                return new CommonResponseDto(ResCode.INVALID, 'accessToken非法')
            }
            const userId = strArr[2]
            try {
                let token = await this.commonRedisService.get(AppConfig.ApiHeaderKey + '_' + userId)
                if (!token) {
                    token = encrypt(`${userId}_${uuid(32)}`, AppConfig.ApiTokenSecretKey)
                    await this.commonRedisService.set(AppConfig.ApiHeaderKey + '_' + userId, token, AppConfig.ApiTokenExpireSec)
                }
                return new CommonResponseDto(ResCode.OK, 'success', token)
            } catch(e){
                console.log(e)
                return new CommonResponseDto(ResCode.ERROR, '服务异常')
            }
        } catch(e){
            console.log(e)
            return new CommonResponseDto(ResCode.ERROR, '服务异常')
        }
    }

    /**
     * 通过api-token获取模型列表，模型列表按模型分组，每个模型里包含了厂商列表
     * @param headers
     * @param res
     */
    @Get('/model-list')
    async getModelList(@Headers() headers: Record<string, string>, @Res({ passthrough: true }) res: Response) {
        res.status(HttpStatus.OK);
        const apiToken = getHeadersApiToken(headers)
        if (!apiToken) {
            return new CommonResponseDto(ResCode.ERROR, '缺失apiToken参数')
        }
        const decryptStr = decrypt(apiToken, AppConfig.ApiTokenSecretKey)
        const strArr = decryptStr.split('_')
        if (strArr.length !== 2) {
            return new CommonResponseDto(ResCode.INVALID, 'apiToken非法')
        } else {
            const userId: string = strArr[0]
            const token = await this.commonRedisService.get(AppConfig.ApiHeaderKey + '_' + userId)
            if (!token || token !== apiToken) {
                return new CommonResponseDto(ResCode.INVALID, 'apiToken非法')
            }
        }
        return new CommonResponseDto(ResCode.OK, 'success', AppConfig.ModelList)

        /*
        const accessToken = headers.accesstoken || headers.accessToken || headers['access-token'] || headers['Access-Token'] || ''
        if (!accessToken) {
            return new CommonResponseDto(ResCode.ERROR, '缺失accessToken参数')
        }
        let decryptStr = decrypt(accessToken, AppConfig.Auth2AccessTokenSecretKey)
        let strArr = decryptStr.split('_')
        if (strArr.length !== 3) {
            return new CommonResponseDto(ResCode.INVALID, 'accessToken非法')
        }
        const appId = strArr[0]
        const openId = strArr[2]
        try {
            const redisAccessToken = await this.commonRedisService.get(getRedisAccessTokenKey(appId, openId))
            if (!redisAccessToken) {
                return new CommonResponseDto(ResCode.EXPIRE, 'accessToken过期或不存在')
            }
            const list = getModelConfigList(false)
            return new CommonResponseDto(ResCode.OK, 'success', list)
        } catch(e){
            console.log(e)
            return new CommonResponseDto(ResCode.ERROR, '服务异常')
        }
         */
    }

    @Get('/refresh-token')
    async refreshAccessToken(@Query() query: {appId: string, refreshToken: string}, @Res({ passthrough: true }) res: Response) {
        res.status(HttpStatus.OK);
        if (!query.appId || !query.refreshToken) {
            return new CommonResponseDto(ResCode.ERROR, '缺失appId或refreshToken参数')
        }
        let decryptStr = decrypt(query.refreshToken, AppConfig.Auth2RefreshTokenSecretKey)
        let strArr = decryptStr.split('_')
        if (strArr.length !== 3) {
            return new CommonResponseDto(ResCode.INVALID, 'refreshToken非法')
        }
        try {
            const appDto = await this.appListService.getOneByAppId(query.appId)
            if (!appDto) {
                return new CommonResponseDto(ResCode.ERROR, 'app不存在')
            }
            if (appDto.status !== AppStatus.NORMAL) {
                return new CommonResponseDto(ResCode.ERROR, 'app状态非正常')
            }
        } catch(e){
            console.log(e)
            return new CommonResponseDto(ResCode.ERROR, '校验appId异常')
        }
        const appId = strArr[0]
        const openId = strArr[2]
        if (appId !== query.appId) {
            return new CommonResponseDto(ResCode.INVALID, 'appId非法')
        }
        try {
            const redisRefreshToken = await this.commonRedisService.get(getRedisRefreshTokenKey(appId, openId))
            if (!redisRefreshToken) {
                return new CommonResponseDto(ResCode.EXPIRE, 'refreshToken过期或不存在')
            }
            const redisAccessToken = await this.commonRedisService.get(getRedisAccessTokenKey(appId, openId))
            if (!redisAccessToken) {
                const accessToken = encrypt(`${appId}_${uuid(32)}_${openId}`, AppConfig.Auth2AccessTokenSecretKey)
                await this.commonRedisService.set(getRedisAccessTokenKey(appId, openId), accessToken, AppConfig.Auth2AccessTokenExpireSec)
                return new CommonResponseDto(ResCode.OK, 'success', {
                    accessToken,
                    expiresIn: AppConfig.Auth2AccessTokenExpireSec
                })
            }
            await this.commonRedisService.del(getRedisAccessTokenKey(appId, openId))
            await this.commonRedisService.set(getRedisAccessTokenKey(appId, openId), redisAccessToken, AppConfig.Auth2AccessTokenExpireSec)
            return new CommonResponseDto(ResCode.OK, 'success', {
                accessToken: redisAccessToken,
                expiresIn: AppConfig.Auth2AccessTokenExpireSec
            })
        } catch(e){
            console.log(e)
            return new CommonResponseDto(ResCode.ERROR, '服务异常')
        }
    }
}
